// recordkeys.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#include <iostream>
#include <string>
#include <ctime>
#include <fstream>
#include <strstream>
using namespace std;

#include <Windows.h>

HHOOK g_hk = NULL;
const int MAX_SIZE = 256;
const char *g_vk_tbl[MAX_SIZE] = {};
clock_t g_last_clock = 0;
ofstream g_ofs;
clock_t g_total_elapsed_time = 0;

void init_tbl()
{
	//virtual key table
	g_vk_tbl[VK_BACK] = "{backspace}";
	g_vk_tbl[VK_RETURN] = "{enter}";
	g_vk_tbl[VK_SPACE] = " ";
	g_vk_tbl[VK_TAB] = "{tab}";
	g_vk_tbl[VK_F1] = "{F1}";
	g_vk_tbl[VK_F2] = "{F2}";
	g_vk_tbl[VK_F3] = "{F3}";
	g_vk_tbl[VK_F4] = "{F4}";
	g_vk_tbl[VK_F5] = "{F5}";
	g_vk_tbl[VK_F6] = "{F6}";
	g_vk_tbl[VK_F7] = "{F7}";
	g_vk_tbl[VK_F8] = "{F8}";
	g_vk_tbl[VK_F9] = "{F9}";
	g_vk_tbl[VK_F10] = "{F10}";
	g_vk_tbl[VK_F11] = "{F11}";
	g_vk_tbl[VK_F12] = "{F12}";
	g_vk_tbl[VK_F13] = "{F13}";
	g_vk_tbl[VK_F14] = "{F14}";
	g_vk_tbl[VK_F15] = "{F15}";
	g_vk_tbl[VK_F16] = "{F16}";
	g_vk_tbl[VK_DOWN] = "{down}";
	g_vk_tbl[VK_UP] = "{up}";
	g_vk_tbl[VK_LEFT] = "{left}";
	g_vk_tbl[VK_RIGHT] = "{right}";
	g_vk_tbl[VK_HELP] = "{help}";
	g_vk_tbl[VK_ESCAPE] = "{esc}";
	g_vk_tbl[VK_HOME] = "{home}";
	g_vk_tbl[VK_INSERT] = "{insert}";
	g_vk_tbl[VK_DELETE] = "{delete}";
	g_vk_tbl[VK_END] = "{end}";
	g_vk_tbl[VK_PAUSE] = "{break}";
	g_vk_tbl[VK_CAPITAL] = "{capslock}";
	g_vk_tbl[VK_SCROLL] = "{scrolllock}";
	g_vk_tbl[VK_NUMLOCK] = "{numlock}";
	g_vk_tbl[VK_PRINT] = "{prtsc}";
	g_vk_tbl[VK_PRIOR] = "{pgup}";
	g_vk_tbl[VK_NEXT] = "{pgdn}";

//#define VK_OEM_1          0xBA   // ';:' for US
//#define VK_OEM_PLUS       0xBB   // '+' any country
//#define VK_OEM_COMMA      0xBC   // ',' any country
//#define VK_OEM_MINUS      0xBD   // '-' any country
//#define VK_OEM_PERIOD     0xBE   // '.' any country
//#define VK_OEM_2          0xBF   // '/?' for US
//#define VK_OEM_3          0xC0   // '`~' for US
	g_vk_tbl[VK_OEM_1] = ";";
	g_vk_tbl[VK_OEM_PLUS] = "=";
	g_vk_tbl[VK_OEM_COMMA] = ",";
	g_vk_tbl[VK_OEM_MINUS] = "-";
	g_vk_tbl[VK_OEM_PERIOD] = ".";
	g_vk_tbl[VK_OEM_2] = "/";
	g_vk_tbl[VK_OEM_3] = "`";

//#define VK_OEM_4          0xDB  //  '[{' for US
//#define VK_OEM_5          0xDC  //  '\|' for US
//#define VK_OEM_6          0xDD  //  ']}' for US
//#define VK_OEM_7          0xDE  //  ''"' for US
//#define VK_OEM_8          0xDF
	g_vk_tbl[VK_OEM_4] = "{[}";
	g_vk_tbl[VK_OEM_5] = "\\";
	g_vk_tbl[VK_OEM_6] = "{]}";
	g_vk_tbl[VK_OEM_7] = "'";

	//doesn't work here
	g_vk_tbl[VK_LWIN] = "{lwi}";
	g_vk_tbl[VK_RWIN] = "{rwi}";
	g_vk_tbl[VK_APPS] = "{app}";
}

BOOL WINAPI my_handler(DWORD ctrl_type)
{
	switch (ctrl_type)
	{
	case CTRL_C_EVENT:
	case CTRL_BREAK_EVENT:
	case CTRL_CLOSE_EVENT:
	case CTRL_SHUTDOWN_EVENT:
		g_ofs.flush();
		ExitProcess(0);
		return TRUE;
	}
	return FALSE;
}


LRESULT CALLBACK keyborad_proc(int code, WPARAM wp, LPARAM lp)
{
	if (code < 0)
		return CallNextHookEx(g_hk, code, wp, lp);

	KBDLLHOOKSTRUCT *ptr = (KBDLLHOOKSTRUCT*) lp;

	switch (wp)
	{
	case WM_KEYUP:
	case WM_SYSKEYUP:
		break;
	
	default:
		return CallNextHookEx(g_hk, code, wp, lp);
	}

	if (ptr->vkCode == VK_LMENU
		|| ptr->vkCode == VK_RMENU
		|| ptr->vkCode == VK_LSHIFT
		|| ptr->vkCode == VK_RSHIFT
		|| ptr->vkCode == VK_LCONTROL
		|| ptr->vkCode == VK_RCONTROL)
	{
		return CallNextHookEx(g_hk, code, wp, lp);
	}

	bool is_cap_pressed;
	short status = GetAsyncKeyState(VK_CAPITAL);
	is_cap_pressed = (status != 0) ? true : false;
	
	string s;

	short status2 = GetAsyncKeyState(VK_RMENU);
	status = GetAsyncKeyState(VK_LMENU);

	if ((status < 0) || status2 < 0)
		s = "%";

	status = GetAsyncKeyState(VK_LSHIFT);
	status2 = GetAsyncKeyState(VK_RSHIFT);
	if (status < 0 || status2 < 0)
		s = s + "+";

	status = GetAsyncKeyState(VK_LCONTROL);
	status2 = GetAsyncKeyState(VK_RCONTROL);
	if (status < 0 || status2 < 0)
		s = s + "^";

	const char *ps = g_vk_tbl[ptr->vkCode];
	if (!ps)
	{
		char c;
		if (is_cap_pressed)
			c = toupper((char)ptr->vkCode);
		else
			c = tolower((char)ptr->vkCode);
		s = s + c;
	}
	else
	{
		s = s + ps;
	}
	
	clock_t elapsed_time = 0;
	if (g_last_clock != 0)
	{
		clock_t now = clock();
		elapsed_time = now - g_last_clock;
		g_last_clock = now;
	}
	else
	{
		g_last_clock = clock();
	}

	g_total_elapsed_time += elapsed_time;

	char title[BUFSIZ];
	ZeroMemory(title, BUFSIZ);
	GetWindowText(GetForegroundWindow(), title, BUFSIZ);

	ostrstream cmt;
	cmt << "'total elapsed time: " << g_total_elapsed_time
		<< " ms  " <<  (g_total_elapsed_time/1000) 
		<< " seconds  " << ((g_total_elapsed_time/1000)/60)
		<< " minutes" << endl << ends;

	cout << elapsed_time << "ms  " << s << "  " 
		<< title << "  total "
		<< (g_total_elapsed_time/1000) << " seconds"
		<< endl;

	g_ofs << endl;
	g_ofs << "wscript.sleep(" << elapsed_time << ")" << endl;
	g_ofs << "sh.appactivate(\"" << title << "\")" << endl;
	g_ofs << "sh.sendkeys(\"" << s << "\")" << endl;
	g_ofs << cmt.str();
	g_ofs << endl;

	return CallNextHookEx(g_hk, code, wp, lp);
}

int _tmain(int argc, _TCHAR* argv[])
{
	init_tbl();

	g_ofs.open("playback.vbe");

	g_ofs << "set sh = wscript.createobject(\"wscript.shell\")" << endl;

	g_hk = SetWindowsHookEx(WH_KEYBOARD_LL, keyborad_proc, 
		GetModuleHandle(NULL), 0);

	if (!g_hk)
	{
		cout << "failed to install hook, reason: " << GetLastError() << endl;
		return 1;
	}

	SetConsoleCtrlHandler(my_handler, TRUE);

	MSG msg;
	while(GetMessage(&msg, NULL, 0, 0 ))
	{
		::TranslateMessage(&msg);
		::DispatchMessage(&msg);
	}

	return 0;
}

